/**
 * Project Name:template-web
 * File Name   :IdentityCardUtils.java
 * Package Name:org.cn.template.component.utils
 * Date:2015年9月27日下午4:15:29
 * Copyright (c) 2015, http://my.oschina.net/httpssl All Rights Reserved.
 *
*/

package org.cn.template.component.utils;

import java.util.Calendar;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.cn.template.component.enums.GenderEnum;

/**
 * ClassName:IdentityCardUtils <br/>
 * Function :TODO ADD FUNCTION. <br/>
 * Reason	:TODO ADD REASON. <br/>
 * Date     :2015年9月27日 下午4:15:29 <br/>
 * @author  :http://my.oschina.net/httpssl
 * @email   :491835898@QQ.COM
 * @since   :JDK 1.7
 * @see 	 
 */
public class IdentityCardUtil
{
	/**
	 * 身份证号码验证 1、号码的结构 公民身份号码是特征组合码，由十七位数字本体码和一位校验码组成。排列顺序从左至右依次为：六位数字地址码，
	 * 八位数字出生日期码，三位数字顺序码和一位数字校验码。 2、地址码(前六位数）
	 * 表示编码对象常住户口所在县(市、旗、区)的行政区划代码，按GB/T2260的规定执行。 3、出生日期码（第七位至十四位）
	 * 表示编码对象出生的年、月、日，按GB/T7408的规定执行，年、月、日代码之间不用分隔符。 4、顺序码（第十五位至十七位）
	 * 表示在同一地址码所标识的区域范围内，对同年、同月、同日出生的人编定的顺序号， 顺序码的奇数分配给男性，偶数分配给女性。 5、校验码（第十八位数）
	 * （1）十七位数字本体码加权求和公式 S = Sum(Ai * Wi), i = 0, ... , 16 ，先对前17位数字的权求和
	 * Ai:表示第i位置上的身份证号码数字值 Wi:表示第i位置上的加权因子 Wi: 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4
	 * 2 （2）计算模 Y = mod(S, 11) （3）通过模得到对应的校验码 Y: 0 1 2 3 4 5 6 7 8 9 10 校验码: 1 0
	 * X 9 8 7 6 5 4 3 2
	 */

	private static final int[] wi = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 };

	/**
	 * 校验位
	 */
	private static final String[] valCodeArr = { "1", "0", "X", "9", "8", "7", "6", "5", "4", "3",
			"2" };

	public static boolean valid(String certNo) {
		if (isEmpty(certNo)) {
			return false;
		}
		// 校验长度
		if (!length(certNo)) {
			return false;
		}

		// 数字位校验
		if (!isNumber(certNo)) {
			return false;
		}

		if (!birthday(certNo)) {
			return false;
		}

		String c = get15Or17(certNo);

		int temp = 0;

		for (int i = 0; i < 17; i++) {
			temp += Integer.parseInt(String.valueOf(c.charAt(i))) * wi[i];
		}
		int mod = temp % 11;

		String cardNo = c + valCodeArr[mod];

		if (cardNo.length() == 18) {
			if (certNo.equalsIgnoreCase(cardNo)) {
				return true;
			}
			return false;
		}

		return false;

	}

	/**
	 * 校验生日
	 * 
	 * @param certNo
	 * @return boolean
	 */
	public static boolean birthday(String certNo) {

		String birthday = getBirthday(certNo);

		if (StringUtils.isBlank(birthday)) {
			return false;
		}

		// Matcher m =
		// Pattern.compile("^((19[0-9]{2})|(20[0-1]{1}[0-4]{1}))-\\d{1,2}-\\d{1,2}$").matcher(birthday);
		//
		// if(!m.matches()){
		// return false;
		// }
		return true;
	}

	/**
	 * 获取生日
	 * 
	 * @param certNo
	 * @return String
	 */
	public static String getBirthday(String certNo) {

		certNo = get15Or17(certNo);
		if (StringUtils.isBlank(certNo)) {
			return null;
		}

		String year = certNo.substring(6, 10);
		String month = certNo.substring(10, 12);
		String day = certNo.substring(12, 14);

		int y = Calendar.getInstance().get(Calendar.YEAR);

		if (!NumberUtils.isNumber(year)) {
			return null;
		}

		int temp = y - Integer.parseInt(year);

		if (temp > 150 || temp < 1) {
			return null;
		}

		if (!NumberUtils.isNumber(month)) {
			return null;
		}

		temp = Integer.parseInt(month);

		if (temp > 12 || temp < 1) {
			return null;
		}

		if (!NumberUtils.isNumber(day)) {
			return null;
		}

		temp = Integer.parseInt(day);

		if (temp > 31 || temp < 1) {
			return null;
		}

		return year + "-" + month + "-" + day;
	}

	/**
	 * 获取性别
	 * 
	 * @param sertNo
	 * @return GenderEnum
	 */
	public static GenderEnum getGender(String sertNo) {
		if (StringUtils.isBlank(sertNo)) {
			return GenderEnum.defaults;
		}
		int length = sertNo.length();
		String c = Character.valueOf(sertNo.charAt(length - 2)).toString();
		// 身份证倒数第二位偶数为女，奇数为男
		int sex = Integer.valueOf(c);
		if (sex % 2 == 0) {
			return GenderEnum.female;
		}
		return GenderEnum.male;
	}

	/**
	 * 校验年龄
	 * 
	 * @param certNo
	 * @return int
	 * @throws Exception
	 */
	public static int getAge(String certNo) throws Exception {

		String birthday = getBirthday(certNo);

		Calendar c = Calendar.getInstance();

		int year = c.get(Calendar.YEAR);

		String[] birthdays = birthday.split("-");

		if (birthdays.length < 3) {
			throw new Exception("身份证格式不正确");
		}

		String y = birthdays[0];

		if (!NumberUtils.isNumber(y)) {
			throw new Exception("身份证生日年份不正确");
		}

		return year - Integer.parseInt(y);

	}

	public static boolean isEmpty(String certNo) {
		if (StringUtils.isBlank(certNo)) {
			return true;
		}
		return false;
	}

	/**
	 * 校验是否是纯数字
	 * 
	 * @param certNo
	 * @return boolean
	 */
	public static boolean isNumber(String certNo) {
		certNo = get15Or17(certNo);
		if (isEmpty(certNo)) {
			return false;
		}
		if (NumberUtils.isNumber(certNo)) {
			return true;
		}
		return false;
	}

	/**
	 * 如果是18位证件号返回前17位，15位返回全部
	 * 
	 * @param certNo
	 * @return boolean
	 */
	public static String get15Or17(String certNo) {
		if (!length(certNo)) {
			return null;
		}
		int length = certNo.length();

		if (length == 18) {
			return certNo.substring(0, 17);
		} else {
			return certNo.substring(0, 6) + "19" + certNo.substring(6, 15);
		}
	}

	/**
	 * 校验长度
	 * 
	 * @param certNo
	 * @return boolean
	 */
	public static boolean length(String certNo) {
		if (certNo.length() != 15 && certNo.length() != 18) {
			return false;
		}
		return true;
	}
}
